// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`bound.js 1`] = `
// refinements of bound vars (closed-over locals)
// should have the same lifetimes as heap objects.

var x : ?string = "xxx";

var tests =
[
  function() {
    var y : string = x;  // not ok
  },

  function() {
    if (x != null) {
      var y : string = x;  // ok
    }
  },

  function() {
    if (x == null) {} else {
      var y : string = x;  // ok
    }
  },

  function() {
    if (x == null)
      return;
    var y : string = x;  // ok
  },

  function() {
    if (!(x != null)) {} else {
      var y : string = x;  // ok
    }
  },

  /* TODO we actually allow this currently; fix
  // requires further remedial work in Env
  function() {
    if (x != null) {
      alert("");
      var y : string = x;  // not ok
    }
  },
  */
  function() {
    if (x != null) {}
    var y : string = x;  // not ok
  },

  function() {
    if (x != null) {
    } else {
      var y : string = x;  // not ok
    }
  },

  function() {
    var y : string = x != null ? x : ""; // ok
  },

  function() {
    var y : string = x || ""; // ok
  },
];
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// refinements of bound vars (closed-over locals)
// should have the same lifetimes as heap objects.

var x: ?string = "xxx";

var tests = [
  function() {
    var y: string = x; // not ok
  },

  function() {
    if (x != null) {
      var y: string = x; // ok
    }
  },

  function() {
    if (x == null) {
    } else {
      var y: string = x; // ok
    }
  },

  function() {
    if (x == null) return;
    var y: string = x; // ok
  },

  function() {
    if (!(x != null)) {
    } else {
      var y: string = x; // ok
    }
  },

  /* TODO we actually allow this currently; fix
  // requires further remedial work in Env
  function() {
    if (x != null) {
      alert("");
      var y : string = x;  // not ok
    }
  },
  */
  function() {
    if (x != null) {
    }
    var y: string = x; // not ok
  },

  function() {
    if (x != null) {
    } else {
      var y: string = x; // not ok
    }
  },

  function() {
    var y: string = x != null ? x : ""; // ok
  },

  function() {
    var y: string = x || ""; // ok
  }
];

`;

exports[`heap.js 1`] = `
var tests =
[
  function() {
    var x : {p:?string} = {p:"xxx"};
    var y : string = x.p;  // not ok
  },

  function() {
    var x : {p:?string} = {p:"xxx"};
    if (x.p != null) {
      var y : string = x.p;  // ok
    }
  },

  function() {
    var x : {p:?string} = {p:"xxx"};
    if (x.p == null) {} else {
      var y : string = x.p;  // ok
    }
  },

  function() {
    var x : {p:?string} = {p:"xxx"};
    if (x.p == null)
      return;
    var y : string = x.p;  // ok
  },

  function() {
    var x : {p:?string} = {p:"xxx"};
    if (!(x.p != null)) {} else {
      var y : string = x.p;  // ok
    }
  },

  function() {
    var x : {p:?string} = {p:"xxx"};
    if (x.p != null) {
      alert("");
      var y : string = x.p;  // not ok
    }
  },

  function () {
    var x : {p:?string} = {p:"xxx"};
    if (x.p != null) {
      x.p = null;
      var y : string = x.p;  // not ok
    }
  },

  function() {
    var x : {p:?string} = {p:"xxx"};
    if (x.p != null) {}
    var y : string = x.p;  // not ok
  },

  function() {
    var x : {p:?string} = {p:"xxx"};
    if (x.p != null) {
    } else {
      var y : string = x.p;  // not ok
    }
  },

  function() {
    var x : {p:?string} = {p:"xxx"};
    var y : string = x.p != null ? x.p : ""; // ok
  },

  function() {
    var x : {p:?string} = {p:"xxx"};
    var y : string = x.p || ""; // ok
  },

  function() {
    var x : {p:string | string[]} = {p:["xxx"]};
    if (Array.isArray(x.p)) {
      var y : string[] = x.p; // ok
    } else {
      var z : string = x.p; // ok
    }
  },

  function() {
    var x : {y: ?string} = {y: null};
    if (!x.y) {
      x.y = "foo";
    }
    (x.y: string);
  },

  function() {
    var x : {y: ?string} = {y: null};
    if (x.y) {
    } else {
      x.y = "foo";
    }
    (x.y: string);
  },

  function() {
    var x : {y: ?string} = {y: null};
    if (!x.y) {
      x.y = 123; // error
    }
    (x.y: string); // error, this got widened to a number
  },

  function() {
    var x : {y: ?string} = {y: null};
    if (x.y) {
      x.y = "foo";
    } else {
      x.y = "bar";
    }
    (x.y : string);
  },

  function() {
    var x : {y: string | number | boolean} = {y: false};
    if (typeof x.y == "number") {
      x.y = "foo";
    }
    (x.y : string); // error, could also be boolean
  },

  function() {
    var x : {y: string | number | boolean} = {y: false};
    if (typeof x.y == "number") {
      x.y = "foo";
    } else if (typeof x.y == "boolean") {
      x.y = "bar";
    }
    (x.y : boolean); // error, string
  },

  function() {
    var x : {y: ?string} = {y: null};
    if (!x.y) {
      x.y = "foo";
    }
    if (x.y) {
      x.y = null;
    }
    (x.y : string); // error
  },

  function() {
    var x : {y: string | number | boolean} = {y: false};
    if (typeof x.y == "number") {
      x.y = "foo";
    }
    // now x.y can is string | boolean
    if (typeof x.y == "string") {
      x.y = false;
    }
    // now x.y is only boolean
    (x.y : string); // error
  },

  function() {
    var x : {y: string | number | boolean} = {y: false};
    if (typeof x.y == "number") {
      x.y = "foo";
    }
    // now x.y can is string | boolean
    if (typeof x.y == "string") {
      x.y = 123;
    }
    // now x.y is number | boolean
    (x.y : string); // error
  },

  function() {
    var x : {y: ?string} = {y: null};
    var z : string = "foo";
    if (x.y) {
      x.y = z;
    } else {
      x.y = z;
    }
    (x.y : string);
  },

  function(x: string) {
    if (x === 'a') {}
    (x: 'b'); // error (but only once, string !~> 'b'; 'a' is irrelevant)
  },

  function(x: mixed) {
    if (typeof x.bar === 'string') {} // error, so \`x.bar\` refinement is empty
    (x: string & number);
  },

  // --- nested conditionals ---
  // after a branch, the current scope may have changed. this causes the
  // subsequent assignment to refine the new scope. these tests make sure that
  // the scope that gets merged after the if statement is the correct
  // post-condition scope, not the one that was saved at the beginning of the
  // if statement.

  function() {
    let x: { foo: ?string } = { foo: null };
    if (!x.foo) {
      if (false) {}
      x.foo = "foo";
    }
    (x.foo: string);
  },

  function() {
    let x: { foo: ?string } = { foo: null };
    if (!x.foo) {
      while(false) {}
      x.foo = "foo";
    }
    (x.foo: string);
  },

  function() {
    let x: { foo: ?string } = { foo: null };
    if (!x.foo) {
      for (var i = 0; i < 0; i++) {}
      x.foo = "foo";
    }
    (x.foo: string);
  },

  function() {
    var x : {p:?string} = {p:"xxx"};
    if (x.p != null) {
      var {p} = x; // TODO: annot checked against type of x
      (p : string); // ok
    }
  },
];
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
var tests = [
  function() {
    var x: { p: ?string } = { p: "xxx" };
    var y: string = x.p; // not ok
  },

  function() {
    var x: { p: ?string } = { p: "xxx" };
    if (x.p != null) {
      var y: string = x.p; // ok
    }
  },

  function() {
    var x: { p: ?string } = { p: "xxx" };
    if (x.p == null) {
    } else {
      var y: string = x.p; // ok
    }
  },

  function() {
    var x: { p: ?string } = { p: "xxx" };
    if (x.p == null) return;
    var y: string = x.p; // ok
  },

  function() {
    var x: { p: ?string } = { p: "xxx" };
    if (!(x.p != null)) {
    } else {
      var y: string = x.p; // ok
    }
  },

  function() {
    var x: { p: ?string } = { p: "xxx" };
    if (x.p != null) {
      alert("");
      var y: string = x.p; // not ok
    }
  },

  function() {
    var x: { p: ?string } = { p: "xxx" };
    if (x.p != null) {
      x.p = null;
      var y: string = x.p; // not ok
    }
  },

  function() {
    var x: { p: ?string } = { p: "xxx" };
    if (x.p != null) {
    }
    var y: string = x.p; // not ok
  },

  function() {
    var x: { p: ?string } = { p: "xxx" };
    if (x.p != null) {
    } else {
      var y: string = x.p; // not ok
    }
  },

  function() {
    var x: { p: ?string } = { p: "xxx" };
    var y: string = x.p != null ? x.p : ""; // ok
  },

  function() {
    var x: { p: ?string } = { p: "xxx" };
    var y: string = x.p || ""; // ok
  },

  function() {
    var x: { p: string | string[] } = { p: ["xxx"] };
    if (Array.isArray(x.p)) {
      var y: string[] = x.p; // ok
    } else {
      var z: string = x.p; // ok
    }
  },

  function() {
    var x: { y: ?string } = { y: null };
    if (!x.y) {
      x.y = "foo";
    }
    (x.y: string);
  },

  function() {
    var x: { y: ?string } = { y: null };
    if (x.y) {
    } else {
      x.y = "foo";
    }
    (x.y: string);
  },

  function() {
    var x: { y: ?string } = { y: null };
    if (!x.y) {
      x.y = 123; // error
    }
    (x.y: string); // error, this got widened to a number
  },

  function() {
    var x: { y: ?string } = { y: null };
    if (x.y) {
      x.y = "foo";
    } else {
      x.y = "bar";
    }
    (x.y: string);
  },

  function() {
    var x: { y: string | number | boolean } = { y: false };
    if (typeof x.y == "number") {
      x.y = "foo";
    }
    (x.y: string); // error, could also be boolean
  },

  function() {
    var x: { y: string | number | boolean } = { y: false };
    if (typeof x.y == "number") {
      x.y = "foo";
    } else if (typeof x.y == "boolean") {
      x.y = "bar";
    }
    (x.y: boolean); // error, string
  },

  function() {
    var x: { y: ?string } = { y: null };
    if (!x.y) {
      x.y = "foo";
    }
    if (x.y) {
      x.y = null;
    }
    (x.y: string); // error
  },

  function() {
    var x: { y: string | number | boolean } = { y: false };
    if (typeof x.y == "number") {
      x.y = "foo";
    }
    // now x.y can is string | boolean
    if (typeof x.y == "string") {
      x.y = false;
    }
    // now x.y is only boolean
    (x.y: string); // error
  },

  function() {
    var x: { y: string | number | boolean } = { y: false };
    if (typeof x.y == "number") {
      x.y = "foo";
    }
    // now x.y can is string | boolean
    if (typeof x.y == "string") {
      x.y = 123;
    }
    // now x.y is number | boolean
    (x.y: string); // error
  },

  function() {
    var x: { y: ?string } = { y: null };
    var z: string = "foo";
    if (x.y) {
      x.y = z;
    } else {
      x.y = z;
    }
    (x.y: string);
  },

  function(x: string) {
    if (x === "a") {
    }
    (x: "b"); // error (but only once, string !~> 'b'; 'a' is irrelevant)
  },

  function(x: mixed) {
    if (typeof x.bar === "string") {
    } // error, so \`x.bar\` refinement is empty
    (x: string & number);
  },

  // --- nested conditionals ---
  // after a branch, the current scope may have changed. this causes the
  // subsequent assignment to refine the new scope. these tests make sure that
  // the scope that gets merged after the if statement is the correct
  // post-condition scope, not the one that was saved at the beginning of the
  // if statement.

  function() {
    let x: { foo: ?string } = { foo: null };
    if (!x.foo) {
      if (false) {
      }
      x.foo = "foo";
    }
    (x.foo: string);
  },

  function() {
    let x: { foo: ?string } = { foo: null };
    if (!x.foo) {
      while (false) {}
      x.foo = "foo";
    }
    (x.foo: string);
  },

  function() {
    let x: { foo: ?string } = { foo: null };
    if (!x.foo) {
      for (var i = 0; i < 0; i++) {}
      x.foo = "foo";
    }
    (x.foo: string);
  },

  function() {
    var x: { p: ?string } = { p: "xxx" };
    if (x.p != null) {
      var { p } = x; // TODO: annot checked against type of x
      (p: string); // ok
    }
  }
];

`;

exports[`lex.js 1`] = `
function block_scope(x: string | number) {
  {
    let x;
    x = ""; // doesn't refine outer x
  }
  (x : string); // error: number ~> string
}

function switch_scope(x: string | number) {
  switch (x) {
    default:
      let x;
      x = ""; // doesn't refine outer x
  }
  (x : string); // error: number ~> string
}

function try_scope(x: string | number) {
  try {
    let x;
    x = ""; // doesn't refine outer x
  } catch (e) {
    x = ""; // refinement would only escape if both sides refined
  }
  (x : string); // error: number ~> string
}

function try_scope_catch(x: string | number) {
  try {
    x = ""; // refinement would only escape if both sides refined
  } catch (e) {
    let x;
    x = ""; // doesn't refine outer x
  }
  (x : string); // error: number ~> string
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
function block_scope(x: string | number) {
  {
    let x;
    x = ""; // doesn't refine outer x
  }
  (x: string); // error: number ~> string
}

function switch_scope(x: string | number) {
  switch (x) {
    default:
      let x;
      x = ""; // doesn't refine outer x
  }
  (x: string); // error: number ~> string
}

function try_scope(x: string | number) {
  try {
    let x;
    x = ""; // doesn't refine outer x
  } catch (e) {
    x = ""; // refinement would only escape if both sides refined
  }
  (x: string); // error: number ~> string
}

function try_scope_catch(x: string | number) {
  try {
    x = ""; // refinement would only escape if both sides refined
  } catch (e) {
    let x;
    x = ""; // doesn't refine outer x
  }
  (x: string); // error: number ~> string
}

`;

exports[`local.js 1`] = `
var paths =
[
  function() {
    var x : ?string = "xxx";
    var y : string = x;  // not ok
  },

  function() {
    var x : ?string = "xxx";
    if (x != null) {
      var y : string = x;  // ok
    }
  },

  function() {
    var x : ?string = "xxx";
    if (x == null) {} else {
      var y : string = x;  // ok
    }
  },

  function() {
    var x : ?string = "xxx";
    if (x == null)
      return;
    var y : string = x;  // ok
  },

  function() {
    var x : ?string = "xxx";
    if (!(x != null)) {} else {
      var y : string = x;  // ok
    }
  },

  function() {
    var x : ?string = "xxx";
    if (x != null) {
      alert("");
      var y : string = x;  // ok
    }
  },

  function() {
    var x : ?string = "xxx";
    if (x != null) {}
    var y : string = x;  // not ok
  },

  function() {
    var x : ?string = "xxx";
    if (x != null) {
    } else {
      var y : string = x;  // not ok
    }
  },

  function() {
    var x : ?string = "xxx";
    var y : string = x != null ? x : ""; // ok
  },

  function() {
    var x : ?string = "xxx";
    var y : string = x || ""; // ok
  },

  function() {
    var x : string | string[] = ["xxx"];
    if (Array.isArray(x)) {
      var y : string[] = x; // ok
    } else {
      var z : string = x; // ok
    }
  },

  function() {
    var x : ?string = null;
    if (!x) {
      x = "xxx";
    }
    var y : string = x;
  },
];
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
var paths = [
  function() {
    var x: ?string = "xxx";
    var y: string = x; // not ok
  },

  function() {
    var x: ?string = "xxx";
    if (x != null) {
      var y: string = x; // ok
    }
  },

  function() {
    var x: ?string = "xxx";
    if (x == null) {
    } else {
      var y: string = x; // ok
    }
  },

  function() {
    var x: ?string = "xxx";
    if (x == null) return;
    var y: string = x; // ok
  },

  function() {
    var x: ?string = "xxx";
    if (!(x != null)) {
    } else {
      var y: string = x; // ok
    }
  },

  function() {
    var x: ?string = "xxx";
    if (x != null) {
      alert("");
      var y: string = x; // ok
    }
  },

  function() {
    var x: ?string = "xxx";
    if (x != null) {
    }
    var y: string = x; // not ok
  },

  function() {
    var x: ?string = "xxx";
    if (x != null) {
    } else {
      var y: string = x; // not ok
    }
  },

  function() {
    var x: ?string = "xxx";
    var y: string = x != null ? x : ""; // ok
  },

  function() {
    var x: ?string = "xxx";
    var y: string = x || ""; // ok
  },

  function() {
    var x: string | string[] = ["xxx"];
    if (Array.isArray(x)) {
      var y: string[] = x; // ok
    } else {
      var z: string = x; // ok
    }
  },

  function() {
    var x: ?string = null;
    if (!x) {
      x = "xxx";
    }
    var y: string = x;
  }
];

`;

exports[`null_tests.js 1`] = `
var null_tests =
[
  // expr != null
  function() {
    var x : ?string = "xxx";
    if (x != null) {
      var y : string = x;  // ok
    }
  },

  function() {
    var x : ?string = "xxx";
    if (null != x) {
      var y : string = x;  // ok
    }
  },

  function() {
    var x : {p:?string} = {p:"xxx"};
    if (x.p != null) {
      var y : string = x.p;  // ok
    }
  },

  function() {
    var x : {p:{q:?string}} = {p:{q:"xxx"}};
    if (x.p.q != null) {
      var y : string = x.p.q;  // ok
    }
  },

  // expr == null
  function() {
    var x : ?string = "xxx";
    if (x == null) {} else {
      var y : string = x;  // ok
    }
  },

  function() {
    var x : {p:?string} = {p:"xxx"};
    if (x.p == null) {} else {
      var y : string = x.p;  // ok
    }
  },

  function() {
    var x : {p:{q:?string}} = {p:{q:"xxx"}};
    if (x.p.q == null) {} else {
      var y : string = x.p.q;  // ok
    }
  },

  // expr !== null
  function() {
    var x : ?string = "xxx";
    if (x !== null) {
      var y : string | void = x;  // ok
    }
  },

  function() {
    var x : {p:?string} = {p:"xxx"};
    if (x.p !== null) {
      var y : string | void = x.p;  // ok
    }
  },

  function() {
    var x : {p:{q:?string}} = {p:{q:"xxx"}};
    if (x.p.q !== null) {
      var y : string | void = x.p.q;  // ok
    }
  },

  // expr === null
  function() {
    var x : ?string = "xxx";
    if (x === null) {} else {
      var y : string | void = x;  // ok
    }
  },

  function() {
    var x : {p:?string} = {p:"xxx"};
    if (x.p === null) {} else {
      var y : string | void = x.p;  // ok
    }
  },

  function() {
    var x : {p:{q:?string}} = {p:{q:"xxx"}};
    if (x.p.q === null) {} else {
      var y : string | void = x.p.q;  // ok
    }
  },
];

// this.p op null
class C {
  p: ?string;

  ensure0(): string {
    if (this.p != null)
      return this.p;
    else
      return "";
  }

  ensure1(): string {
    if (this.p == null)
      return "";
    return this.p;
  }

  ensure2(): string | void {
    if (this.p !== null)
      return this.p;
    else
      return "";
  }

  ensure3(): string | void {
    if (this.p === null)
      return "";
    return this.p;
  }
}

// super.p op null
class D extends C {

  ensure100(): string {
    if (super.p != null)
      return super.p;
    else
      return "";
  }

  ensure101(): string {
    if (super.p == null)
      return "";
    else
      return super.p;
  }

  ensure103(): string {
    if (super.p != null) {
      alert("");
      return super.p;  // not ok
    }
    return "";
  }
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
var null_tests = [
  // expr != null
  function() {
    var x: ?string = "xxx";
    if (x != null) {
      var y: string = x; // ok
    }
  },

  function() {
    var x: ?string = "xxx";
    if (null != x) {
      var y: string = x; // ok
    }
  },

  function() {
    var x: { p: ?string } = { p: "xxx" };
    if (x.p != null) {
      var y: string = x.p; // ok
    }
  },

  function() {
    var x: { p: { q: ?string } } = { p: { q: "xxx" } };
    if (x.p.q != null) {
      var y: string = x.p.q; // ok
    }
  },

  // expr == null
  function() {
    var x: ?string = "xxx";
    if (x == null) {
    } else {
      var y: string = x; // ok
    }
  },

  function() {
    var x: { p: ?string } = { p: "xxx" };
    if (x.p == null) {
    } else {
      var y: string = x.p; // ok
    }
  },

  function() {
    var x: { p: { q: ?string } } = { p: { q: "xxx" } };
    if (x.p.q == null) {
    } else {
      var y: string = x.p.q; // ok
    }
  },

  // expr !== null
  function() {
    var x: ?string = "xxx";
    if (x !== null) {
      var y: string | void = x; // ok
    }
  },

  function() {
    var x: { p: ?string } = { p: "xxx" };
    if (x.p !== null) {
      var y: string | void = x.p; // ok
    }
  },

  function() {
    var x: { p: { q: ?string } } = { p: { q: "xxx" } };
    if (x.p.q !== null) {
      var y: string | void = x.p.q; // ok
    }
  },

  // expr === null
  function() {
    var x: ?string = "xxx";
    if (x === null) {
    } else {
      var y: string | void = x; // ok
    }
  },

  function() {
    var x: { p: ?string } = { p: "xxx" };
    if (x.p === null) {
    } else {
      var y: string | void = x.p; // ok
    }
  },

  function() {
    var x: { p: { q: ?string } } = { p: { q: "xxx" } };
    if (x.p.q === null) {
    } else {
      var y: string | void = x.p.q; // ok
    }
  }
];

// this.p op null
class C {
  p: ?string;

  ensure0(): string {
    if (this.p != null) return this.p;
    else return "";
  }

  ensure1(): string {
    if (this.p == null) return "";
    return this.p;
  }

  ensure2(): string | void {
    if (this.p !== null) return this.p;
    else return "";
  }

  ensure3(): string | void {
    if (this.p === null) return "";
    return this.p;
  }
}

// super.p op null
class D extends C {
  ensure100(): string {
    if (super.p != null) return super.p;
    else return "";
  }

  ensure101(): string {
    if (super.p == null) return "";
    else return super.p;
  }

  ensure103(): string {
    if (super.p != null) {
      alert("");
      return super.p; // not ok
    }
    return "";
  }
}

`;

exports[`switch.js 1`] = `
// @flow
function foo(a,b,c) {
  switch (c) {
  case a.x.y: // OK
  case b.x.y: // OK
    return;
  default:
    return;
  }
}

// test refis out of switches that are exhaustive without default case

function exhaustion1(x): number {
  var foo;
  switch (x) {
  case 0: // falls through
  case 1:
    foo = 3;
    break;
  default:
    throw new Error('Invalid state');
  }
  return foo; // no error
}

function exhaustion2(x, y): number {
  var foo;
  switch (x) {
  case 0:
    if (y) {
      break;  // leaks uninitialized foo out of switch
    }
    /**
     * TODO this shouldn't cause an error, because the path that
     * runs it will always go on to assign a number to foo. But
     * we'll need true isolation in env snapshots to model this.
     * Currently tvars are always shared between clones - we'd
     * have to rework envs pretty extensively to avoid it.
     *
    foo = "";
     */
  case 1:
    foo = 3;
    break;
  default:
    throw new Error('Invalid state');
  }
  return foo; // error, possibly uninitialized
}

function exhaustion3(x): number {
  let foo = null;
  switch (x) {
  case 0: // falls through
  case 1:
    foo = 3;
    break;
  default:
    throw new Error('Invalid state');
  }
  return foo; // no error
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// @flow
function foo(a, b, c) {
  switch (c) {
    case a.x.y: // OK
    case b.x.y: // OK
      return;
    default:
      return;
  }
}

// test refis out of switches that are exhaustive without default case

function exhaustion1(x): number {
  var foo;
  switch (x) {
    case 0: // falls through
    case 1:
      foo = 3;
      break;
    default:
      throw new Error("Invalid state");
  }
  return foo; // no error
}

function exhaustion2(x, y): number {
  var foo;
  switch (x) {
    case 0:
      if (y) {
        break; // leaks uninitialized foo out of switch
      }
    /**
     * TODO this shouldn't cause an error, because the path that
     * runs it will always go on to assign a number to foo. But
     * we'll need true isolation in env snapshots to model this.
     * Currently tvars are always shared between clones - we'd
     * have to rework envs pretty extensively to avoid it.
     *
    foo = "";
     */
    case 1:
      foo = 3;
      break;
    default:
      throw new Error("Invalid state");
  }
  return foo; // error, possibly uninitialized
}

function exhaustion3(x): number {
  let foo = null;
  switch (x) {
    case 0: // falls through
    case 1:
      foo = 3;
      break;
    default:
      throw new Error("Invalid state");
  }
  return foo; // no error
}

`;

exports[`typeof_tests.js 1`] = `
var null_tests =
[
  // typeof expr == typename
  function() {
    var x : ?string = "xxx";
    if (typeof x == "string") {
      var y : string = x;  // ok
    }
  },

  function() {
    var x : ?string = "xxx";
    if ("string" == typeof x) {
      var y : string = x;  // ok
    }
  },

  function() {
    var x : {p:?string} = {p:"xxx"};
    if (typeof x.p == "string") {
      var y : string = x.p;  // ok
    }
  },

  function() {
    var x : {p:{q:?string}} = {p:{q:"xxx"}};
    if (typeof x.p.q == "string") {
      var y : string = x.p.q;  // ok
    }
  },

  // typeof expr != typename
  function() {
    var x : ?string = "xxx";
    if (typeof x != "string") {} else {
      var y : string = x;  // ok
    }
  },

  function() {
    var x : {p:?string} = {p:"xxx"};
    if (typeof x.p != "string") {} else {
      var y : string = x.p;  // ok
    }
  },

  function() {
    var x : {p:{q:?string}} = {p:{q:"xxx"}};
    if (typeof x.p.q != "string") {} else {
      var y : string = x.p.q;  // ok
    }
  },

  // typeof expr === typename
  function() {
    var x : ?string = "xxx";
    if (typeof x === "string") {
      var y : string = x;  // ok
    }
  },

  function() {
    var x : {p:?string} = {p:"xxx"};
    if (typeof x.p === "string") {
      var y : string = x.p;  // ok
    }
  },

  function() {
    var x : {p:{q:?string}} = {p:{q:"xxx"}};
    if (typeof x.p.q === "string") {
      var y : string = x.p.q;  // ok
    }
  },

  // typeof expr !== typename
  function() {
    var x : ?string = "xxx";
    if (typeof x !== "string") {} else {
      var y : string = x;  // ok
    }
  },

  function() {
    var x : {p:?string} = {p:"xxx"};
    if (typeof x.p !== "string") {} else {
      var y : string = x.p;  // ok
    }
  },

  function() {
    var x : {p:{q:?string}} = {p:{q:"xxx"}};
    if (typeof x.p.q !== "string") {} else {
      var y : string = x.p.q;  // ok
    }
  },
];

// typeof this.p op typename
class A {
  p: ?string;

  ensure0(): string {
    if (typeof this.p == "string")
      return this.p;
    else
      return "";
  }

  ensure1(): string {
    if (typeof this.p != "string")
      return "";
    else
     return this.p;
  }

  ensure2(): string | void {
    if (typeof this.p === "string")
      return this.p;
    else
      return "";
  }

  ensure3(): string | void {
    if (typeof this.p !== "string")
      return "";
    else
      return this.p;
  }
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
var null_tests = [
  // typeof expr == typename
  function() {
    var x: ?string = "xxx";
    if (typeof x == "string") {
      var y: string = x; // ok
    }
  },

  function() {
    var x: ?string = "xxx";
    if ("string" == typeof x) {
      var y: string = x; // ok
    }
  },

  function() {
    var x: { p: ?string } = { p: "xxx" };
    if (typeof x.p == "string") {
      var y: string = x.p; // ok
    }
  },

  function() {
    var x: { p: { q: ?string } } = { p: { q: "xxx" } };
    if (typeof x.p.q == "string") {
      var y: string = x.p.q; // ok
    }
  },

  // typeof expr != typename
  function() {
    var x: ?string = "xxx";
    if (typeof x != "string") {
    } else {
      var y: string = x; // ok
    }
  },

  function() {
    var x: { p: ?string } = { p: "xxx" };
    if (typeof x.p != "string") {
    } else {
      var y: string = x.p; // ok
    }
  },

  function() {
    var x: { p: { q: ?string } } = { p: { q: "xxx" } };
    if (typeof x.p.q != "string") {
    } else {
      var y: string = x.p.q; // ok
    }
  },

  // typeof expr === typename
  function() {
    var x: ?string = "xxx";
    if (typeof x === "string") {
      var y: string = x; // ok
    }
  },

  function() {
    var x: { p: ?string } = { p: "xxx" };
    if (typeof x.p === "string") {
      var y: string = x.p; // ok
    }
  },

  function() {
    var x: { p: { q: ?string } } = { p: { q: "xxx" } };
    if (typeof x.p.q === "string") {
      var y: string = x.p.q; // ok
    }
  },

  // typeof expr !== typename
  function() {
    var x: ?string = "xxx";
    if (typeof x !== "string") {
    } else {
      var y: string = x; // ok
    }
  },

  function() {
    var x: { p: ?string } = { p: "xxx" };
    if (typeof x.p !== "string") {
    } else {
      var y: string = x.p; // ok
    }
  },

  function() {
    var x: { p: { q: ?string } } = { p: { q: "xxx" } };
    if (typeof x.p.q !== "string") {
    } else {
      var y: string = x.p.q; // ok
    }
  }
];

// typeof this.p op typename
class A {
  p: ?string;

  ensure0(): string {
    if (typeof this.p == "string") return this.p;
    else return "";
  }

  ensure1(): string {
    if (typeof this.p != "string") return "";
    else return this.p;
  }

  ensure2(): string | void {
    if (typeof this.p === "string") return this.p;
    else return "";
  }

  ensure3(): string | void {
    if (typeof this.p !== "string") return "";
    else return this.p;
  }
}

`;

exports[`undef_tests.js 1`] = `
var undef_tests =
[
  // NOTE: not (yet?) supporting non-strict eq test for undefined

  // expr !== undefined
  function() {
    var x : ?string = "xxx";
    if (x !== undefined && x !== null) {
      var y : string = x;  // ok
    }
  },

  function() {
    var x : ?string = "xxx";
    if (undefined !== x && x !== null) {
      var y : string = x;  // ok
    }
  },

  function() {
    var x : {p:?string} = {p:"xxx"};
    if (x.p !== undefined && x.p !== null) {
      var y : string = x.p;  // ok
    }
  },

  function() {
    var x : {p:{q:?string}} = {p:{q:"xxx"}};
    if (x.p.q !== undefined && x.p.q !== null) {
      var y : string = x.p.q;  // ok
    }
  },

  // expr === undefined
  function() {
    var x : ?string = "xxx";
    if (x === undefined || x === null) {} else {
      var y : string = x;  // ok
    }
  },

  function() {
    var x : {p:?string} = {p:"xxx"};
    if (x.p === undefined || x.p === null) {} else {
      var y : string = x.p;  // ok
    }
  },

  function() {
    var x : {p:{q:?string}} = {p:{q:"xxx"}};
    if (x.p.q === undefined || x.p.q === null) {} else {
      var y : string = x.p.q;  // ok
    }
  },
];

// this.p op undefined
class A {
  p: ?string;

  ensure0(): string {
    if (this.p !== undefined && this.p !== null)
      return this.p;
    else
      return "";
  }

  ensure1(): string {
    if (this.p === undefined || this.p === null)
      return "";
    else
      return this.p;
  }
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
var undef_tests = [
  // NOTE: not (yet?) supporting non-strict eq test for undefined

  // expr !== undefined
  function() {
    var x: ?string = "xxx";
    if (x !== undefined && x !== null) {
      var y: string = x; // ok
    }
  },

  function() {
    var x: ?string = "xxx";
    if (undefined !== x && x !== null) {
      var y: string = x; // ok
    }
  },

  function() {
    var x: { p: ?string } = { p: "xxx" };
    if (x.p !== undefined && x.p !== null) {
      var y: string = x.p; // ok
    }
  },

  function() {
    var x: { p: { q: ?string } } = { p: { q: "xxx" } };
    if (x.p.q !== undefined && x.p.q !== null) {
      var y: string = x.p.q; // ok
    }
  },

  // expr === undefined
  function() {
    var x: ?string = "xxx";
    if (x === undefined || x === null) {
    } else {
      var y: string = x; // ok
    }
  },

  function() {
    var x: { p: ?string } = { p: "xxx" };
    if (x.p === undefined || x.p === null) {
    } else {
      var y: string = x.p; // ok
    }
  },

  function() {
    var x: { p: { q: ?string } } = { p: { q: "xxx" } };
    if (x.p.q === undefined || x.p.q === null) {
    } else {
      var y: string = x.p.q; // ok
    }
  }
];

// this.p op undefined
class A {
  p: ?string;

  ensure0(): string {
    if (this.p !== undefined && this.p !== null) return this.p;
    else return "";
  }

  ensure1(): string {
    if (this.p === undefined || this.p === null) return "";
    else return this.p;
  }
}

`;

exports[`void_tests.js 1`] = `
var void_tests =
[
  // NOTE: not (yet?) supporting non-strict eq test for undefined

  // expr !== void(...)
  function() {
    var x : ?string = "xxx";
    if (x !== void(0) && x !== null) {
      var y : string = x;  // ok
    }
  },

  function() {
    var x : ?string = "xxx";
    if (void(0) !== x && x !== null) {
      var y : string = x;  // ok
    }
  },

  function() {
    var x : {p:?string} = {p:"xxx"};
    if (x.p !== void(0) && x.p !== null) {
      var y : string | void = x.p;  // ok
    }
  },

  function() {
    var x : {p:{q:?string}} = {p:{q:"xxx"}};
    if (x.p.q !== void(0) && x.p.q !== null) {
      var y : string = x.p.q;  // ok
    }
  },

  // expr === void(...)
  function() {
    var x : ?string = "xxx";
    if (x === void(0) || x === null) {} else {
      var y : string = x;  // ok
    }
  },

  function() {
    var x : {p:?string} = {p:"xxx"};
    if (x.p === void(0) || x.p === null) {} else {
      var y : string = x.p;  // ok
    }
  },

  function() {
    var x : {p:{q:?string}} = {p:{q:"xxx"}};
    if (x.p.q === void(0) || x.p.q === null) {} else {
      var y : string = x.p.q;  // ok
    }
  },
];

// this.p op void(...)
class A {
  p: ?string;

  ensure0(): string {
    if (this.p !== void(0) && this.p !== null)
      return this.p;
    else
      return "";
  }

  ensure1(): string {
    if (this.p === void(0) || this.p === null)
      return "";
    else
      return this.p;
  }
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
var void_tests = [
  // NOTE: not (yet?) supporting non-strict eq test for undefined

  // expr !== void(...)
  function() {
    var x: ?string = "xxx";
    if (x !== void 0 && x !== null) {
      var y: string = x; // ok
    }
  },

  function() {
    var x: ?string = "xxx";
    if (void 0 !== x && x !== null) {
      var y: string = x; // ok
    }
  },

  function() {
    var x: { p: ?string } = { p: "xxx" };
    if (x.p !== void 0 && x.p !== null) {
      var y: string | void = x.p; // ok
    }
  },

  function() {
    var x: { p: { q: ?string } } = { p: { q: "xxx" } };
    if (x.p.q !== void 0 && x.p.q !== null) {
      var y: string = x.p.q; // ok
    }
  },

  // expr === void(...)
  function() {
    var x: ?string = "xxx";
    if (x === void 0 || x === null) {
    } else {
      var y: string = x; // ok
    }
  },

  function() {
    var x: { p: ?string } = { p: "xxx" };
    if (x.p === void 0 || x.p === null) {
    } else {
      var y: string = x.p; // ok
    }
  },

  function() {
    var x: { p: { q: ?string } } = { p: { q: "xxx" } };
    if (x.p.q === void 0 || x.p.q === null) {
    } else {
      var y: string = x.p.q; // ok
    }
  }
];

// this.p op void(...)
class A {
  p: ?string;

  ensure0(): string {
    if (this.p !== void 0 && this.p !== null) return this.p;
    else return "";
  }

  ensure1(): string {
    if (this.p === void 0 || this.p === null) return "";
    else return this.p;
  }
}

`;
